package cn.aitrox.ry.app.web.controller.task;

import cn.aitrox.ry.app.web.service.ImageTransferService;
import cn.aitrox.ry.app.web.vo.ResultOutVo;
import cn.aitrox.ry.app.web.vo.task.in.TaskStatusEditInVo;
import cn.aitrox.ry.app.web.vo.task.out.AnnoInfoOutVo;
import cn.aitrox.ry.app.web.vo.task.out.AnnoUserInfoOutVo;
import cn.aitrox.ry.app.web.vo.task.out.AnnoUserOutVo;
import cn.aitrox.ry.app.web.vo.task.out.DataInfoOutVo;
import cn.aitrox.ry.app.web.vo.task.out.TaskDetailOutVo;
import cn.aitrox.ry.app.web.vo.task.out.TaskInfoOutVo;
import cn.aitrox.ry.app.web.vo.task.out.TaskOutVo;
import cn.aitrox.ry.app.web.vo.task.out.TaskSeriesImportOutVo;
import cn.aitrox.ry.bean.CommonPageInfo;
import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.controller.BaseController;
import cn.aitrox.ry.common.service.api.dto.Company;
import cn.aitrox.ry.common.service.api.dto.User;
import cn.aitrox.ry.constant.CommonConstant;
import cn.aitrox.ry.service.anno.dto.task.AnnoUserDto;
import cn.aitrox.ry.service.anno.dto.task.TaskDetailDto;
import cn.aitrox.ry.service.anno.dto.task.TaskDto;
import cn.aitrox.ry.service.anno.dto.task.in.AnnoSeriesDto;
import cn.aitrox.ry.service.anno.dto.task.in.TaskCopyInDto;
import cn.aitrox.ry.service.anno.dto.task.in.TaskCreateInDto;
import cn.aitrox.ry.service.anno.dto.task.in.TaskEditInDto;
import cn.aitrox.ry.service.anno.dto.task.in.TaskNameExistedInDto;
import cn.aitrox.ry.service.anno.dto.task.in.TaskSearchInDto;
import cn.aitrox.ry.service.anno.feign.TaskFeign;
import cn.aitrox.ry.service.dwh.dto.DwhSeriesDto;
import cn.aitrox.ry.service.dwh.dto.in.DwhSeriesQueryInDto;
import cn.aitrox.ry.service.dwh.feign.DwhSeriesFeign;
import cn.aitrox.ry.service.user.dto.in.UserCountInDto;
import cn.aitrox.ry.service.user.dto.in.UserListInDto;
import cn.aitrox.ry.service.user.ennumtype.UserStatusEnum;
import cn.aitrox.ry.service.user.feign.CompanyFeign;
import cn.aitrox.ry.service.user.feign.UserFeign;
import cn.aitrox.ry.util.ArrayUtil;
import cn.aitrox.ry.util.BeanUtil;
import cn.aitrox.ry.util.ExcelUtil;
import cn.aitrox.ry.util.LogUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@RestController
public class TaskController extends BaseController {

    private static final Logger LOG = LoggerFactory.getLogger(TaskController.class);

    @Autowired
    @Lazy
    private TaskFeign taskFeign;

    @Autowired
    @Lazy
    private UserFeign userFeign;

    @Autowired
    @Lazy
    private CompanyFeign companyFeign;

    @Autowired
    @Lazy
    private DwhSeriesFeign dwhSeriesFeign;

    @Autowired
    private ImageTransferService imageTransferService;

    @PostMapping(value = "/anno/task/search")
    public RespResult<CommonPageInfo<TaskOutVo>> search(@RequestBody TaskSearchInDto input) {
        CommonPageInfo<TaskDto> pageInfo = taskFeign.search(input);
        if (ArrayUtil.isEmpty(pageInfo.getList())) {
            return RespResult.ok(pageInfo);
        }

        List<Integer> userIdList = pageInfo.getList().stream().map(TaskDto::getCreateUserId).distinct().collect(Collectors.toList());
        List<User> userList = userFeign.list(new UserListInDto(userIdList));
        Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, x -> x));

        List<Company> allCompany = companyFeign.list();
        Map<Integer, Company> companyMap = allCompany.stream().collect(Collectors.toMap(Company::getId, x -> x));

        List<TaskOutVo> taskOutList = pageInfo.getList().stream().map(x -> {
            TaskOutVo taskOut = new TaskOutVo();
            BeanUtil.copy(x, taskOut);

            User user = userMap.get(x.getCreateUserId());
            List<Company> companyList = null;
            if (!ArrayUtil.isEmpty(x.getCompanyIdList())) {
                companyList = x.getCompanyIdList().stream().map(companyId -> companyMap.get(companyId)).collect(Collectors.toList());
            }

            taskOut.setCreateUserName(null == user ? CommonConstant.EMPTY : user.getUserName());
            taskOut.setCompanyList(companyList);

            return taskOut;

        }).collect(Collectors.toList());

        return RespResult.ok(new CommonPageInfo<>(pageInfo.getTotal(), pageInfo.getPages(), taskOutList));
    }

    @PostMapping(value = "/anno/task/status/edit")
    public RespResult statusEdit(@Validated @RequestBody TaskStatusEditInVo input) {
        return taskFeign.statusEdit(input.getId(), input.getStatus());
    }

    @PostMapping(value = "/anno/task")
    public RespResult<TaskDto> create(@Validated @RequestBody TaskCreateInDto input) {
        RespResult<TaskDto> respResult = taskFeign.create(input);
        if (!respResult._isOk()) {
            return respResult;
        }
        if (null != input.getAnnoUserSetting()) {
            // 触发自动转换
            try {
                imageTransferService.execute(respResult.getData().getId());
            } catch (Exception e) {
                LOG.error(LogUtil.genException(e, "task create trigger image transfer err", "taskId", respResult.getData().getId().toString()), e);
            }
        }
        return respResult;
    }

    @PutMapping(value = "/anno/task")
    public RespResult edit(@Validated @RequestBody TaskEditInDto input) {
        RespResult respResult = taskFeign.edit(input);
        if (!respResult._isOk()) {
            return respResult;
        }
        if (null != input.getAnnoUserSetting()) {
            // 触发自动转换
            try {
                imageTransferService.execute(input.getId());
            } catch (Exception e) {
                LOG.error(LogUtil.genException(e, "task update trigger image transfer err", "taskId", input.getId().toString()), e);
            }
        }
        return respResult;
    }

    @GetMapping(value = "/anno/task/{id}")
    public RespResult<TaskDetailOutVo> read(@PathVariable("id") Integer id) {
        TaskDetailDto taskDetail = taskFeign.read(id);
        if (null == taskDetail) {
            return RespResult.error("数据不存在");
        }

        List<Integer> allUserId = new ArrayList<>();
        allUserId.add(taskDetail.getCreateUserId());

        if (!ArrayUtil.isEmpty(taskDetail.getAnnoUserIdList())) {
            List<Integer> annoUserIdList = taskDetail.getAnnoUserIdList().stream().map(AnnoUserDto::getUserIdList).flatMap(Collection::stream).collect(Collectors.toList());
            allUserId.addAll(annoUserIdList);
        }
        List<User> userList = userFeign.list(new UserListInDto(allUserId.stream().distinct().collect(Collectors.toList())));

        Integer enableUserCount = userFeign.count(new UserCountInDto(null, UserStatusEnum.ENABLE.getStatus()));

        List<Company> companyList = companyFeign.list();

        TaskDetailOutVo taskDetailOut = this.packTaskDetailOut(taskDetail, userList, companyList, enableUserCount);
        return RespResult.ok(taskDetailOut);
    }

    @PostMapping(value = "/anno/task/series/import")
    public RespResult<TaskSeriesImportOutVo> seriesImport(@RequestParam("file") MultipartFile mfile) throws IOException {
        File file = null;
        try {
            String oringFileName = mfile.getOriginalFilename();
            String fileSuffix = oringFileName.substring(oringFileName.lastIndexOf(".") + 1);
            if (!ExcelUtil.XLSX.equalsIgnoreCase(fileSuffix)) {
                return RespResult.error("文件格式错误，只能为xlsx");
            }

            file = new File(UUID.randomUUID().toString() + CommonConstant.FILE_NAME_SEPARATOR + ExcelUtil.XLSX);
            FileUtils.copyInputStreamToFile(mfile.getInputStream(), file);

            // 解析文件
            List<List<String>> excelData = ExcelUtil.readFile(file);

            // 校验
            if (ArrayUtil.isEmpty(excelData) || excelData.size() < 2) {
                return RespResult.error("导入文件无数据");
            }
            if (!ArrayUtil.isEmpty(excelData) && excelData.get(0).size() < 3) {
                return RespResult.error("导入文件必须大于3列");
            }

            List<String> tmpSeriesList = new ArrayList<>();
            List<AnnoSeriesDto> seriesInfoList = excelData.stream().skip(1).map(x -> {
                List<String> auxSeriesList = null;
                List<String> annoAreaList = null;
                if (StringUtils.isNotBlank(x.get(1))) {
                    auxSeriesList = Arrays.asList(x.get(1).trim().split(CommonConstant.FEN_HAO));
                }
                if (StringUtils.isNotBlank(x.get(2))) {
                    annoAreaList = Arrays.asList(x.get(2).trim().split(CommonConstant.FEN_HAO));
                }
                tmpSeriesList.add(x.get(0).trim());
                if (!ArrayUtil.isEmpty(auxSeriesList)) {
                    tmpSeriesList.addAll(auxSeriesList);
                }
                return new AnnoSeriesDto(x.get(0).trim(), auxSeriesList, annoAreaList);
            }).collect(Collectors.toList());

            List<String> allSeriesList = tmpSeriesList.stream().distinct().collect(Collectors.toList());

            // 调用数仓服务，校验序列是否存在
            RespResult<List<DwhSeriesDto>> respResult = dwhSeriesFeign.seriesQuery(new DwhSeriesQueryInDto(allSeriesList));
            if (!respResult._isOk()) {
                return RespResult.error("查询序列信息失败");
            }
            List<String> existedSeriesList = respResult.getData().stream().map(DwhSeriesDto::getSeriesUid).distinct().collect(Collectors.toList());

            allSeriesList.removeAll(existedSeriesList);

            if (!ArrayUtil.isEmpty(allSeriesList)) {
                return RespResult.error("序列不存在: " + StringUtils.joinWith(CommonConstant.DEFAULT_SEPARATOR, allSeriesList.toArray()));
            }
            return RespResult.ok(new TaskSeriesImportOutVo(seriesInfoList));
        } finally {
            if (null == file && file.exists()) {
                file.delete();
            }
        }
    }

    @PostMapping(value = "/anno/task/copy")
    public RespResult copy(@Validated @RequestBody TaskCopyInDto input) {
        return taskFeign.copy(input.getTaskId());
    }

    @PostMapping(value = "/anno/task/name/existed")
    public RespResult<ResultOutVo> taskNameExisted(@Validated @RequestBody TaskNameExistedInDto input) {
        Boolean existed = taskFeign.taskNameExisted(input);
        return RespResult.ok(ResultOutVo.build(existed));
    }

    private TaskDetailOutVo packTaskDetailOut(TaskDetailDto taskDetail, List<User> users, List<Company> companyList, Integer totalUserNum) {

        Map<Integer, User> userMap = users.stream().collect(Collectors.toMap(User::getId, x -> x));
        Map<Integer, Company> companyMap = companyList.stream().collect(Collectors.toMap(Company::getId, x -> x));

        TaskInfoOutVo taskInfo = new TaskInfoOutVo();
        BeanUtil.copy(taskDetail, taskInfo);
        taskInfo.setCreateUser(userMap.get(taskDetail.getCreateUserId()));

        AnnoInfoOutVo annoInfo = new AnnoInfoOutVo();
        BeanUtil.copy(taskDetail, annoInfo);

        DataInfoOutVo dataInfo = new DataInfoOutVo();
        dataInfo.setSeriesList(taskDetail.getSeriesList());

        List<AnnoUserOutVo> annoUserList = null;
        if (!ArrayUtil.isEmpty(taskDetail.getAnnoUserIdList())) {
            annoUserList = taskDetail.getAnnoUserIdList().stream().map(x -> {
                List<User> userList = x.getUserIdList().stream().map(userId -> userMap.get(userId)).collect(Collectors.toList());
                return new AnnoUserOutVo(companyMap.get(x.getCompanyId()), userList);
            }).collect(Collectors.toList());
        }

        AnnoUserInfoOutVo annoUserInfo = new AnnoUserInfoOutVo();
        BeanUtil.copy(taskDetail, annoUserInfo);
        annoUserInfo.setAnnoUserList(annoUserList);
        annoUserInfo.setTotalUserNum(totalUserNum);

        return new TaskDetailOutVo(taskInfo, annoInfo, dataInfo, annoUserInfo);
    }

}
